package com.example.frn.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JwtUtil {
    private static final String SECRET_KEY = "my-secret";//秘钥（盐）
    private static final long EXPIRATION_TIME_THRESHOLD = 1000 * 60 * 30L;//设置token的有效时长（30min）

    /**
     * 生成JWT令牌的方法
     * @param claims 用户的相关信息或其他业务需要的必要信息
     * @return
     */
    public static String build(Map<String, String> claims) {
        // 生成一个JWT Builder，用于构建令牌
        JWTCreator.Builder builder = JWT.create();
        builder.withIssuer("LiYe");
        builder.withSubject("用户登录认证");
        long now = System.currentTimeMillis();//当前系统时间
        builder.withIssuedAt(new Date(now));//签发时间
        builder.withExpiresAt(new Date(now + EXPIRATION_TIME_THRESHOLD));//过期时间
        builder.withJWTId(UUID.randomUUID().toString());//给令牌设定一个唯一标识
        claims.forEach((key, value) -> {
            builder.withClaim(key, value);//将用户的相关信息放入token
        });
        // 1.选择加密算法HMAC256，2.设置加密秘钥my-secret，3.生成token并返回
        return builder.sign(Algorithm.HMAC256(SECRET_KEY));
    }

    /**
     * 核实验证jwt是否合法：1.不为null 2.没有过期 3.不是伪造的
     * @param token JSON WEB TOKEN JWT令牌
     * @return
     */
    // import org.springframework.util.ObjectUtils;
    public static Map<String, String> verify(String token) {
        if (ObjectUtils.isEmpty(token)) {
            throw new RuntimeException("token字符串为null");
        }
        Map<String, String> resultMap = new HashMap<>();
        // 解密JWT: 需要确认加密算法和密钥，两者缺一不可; 如果令牌是伪造的，那么核实过程将抛出异常
        DecodedJWT jwt = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build().verify(token);
        long now = System.currentTimeMillis();//当前系统时间

        // 遍历token中保存的claim信息，重新封装到另一个resultMap里
        Map<String, Claim> claims = jwt.getClaims();
        claims.forEach((key, value) -> {
            resultMap.put(key, value.asString());
        });
        // auto refresh token: 如果令牌即将过期（1min之后），那么重新生成一个新的令牌
        if (jwt.getExpiresAt().getTime() - now <= 1000 * 60 * 1) {
            //排除掉令牌的声明信息
            resultMap.remove("sub");
            resultMap.remove("iss");
            resultMap.remove("exp");
            resultMap.remove("iat");
            resultMap.remove("jti");
            resultMap.put("newToken", build(resultMap));
        }
        return resultMap;
    }
}
